#include "stdafx.h"
#include "cppflibinc.h"


using namespace cppflib;
using namespace cppflib::os;

static void TestNewCFString(void)
{
    cppmempool::MemPoolWrap localPool;

    debug::Print(_S("Testing NewCFString...\n"));

    CFString *s = new CFString("abc");
    assert(s->Compare(_S("abc")) == 0);
    s = new CFString("hello world", 6);
    assert(s->Compare(_S("hello ")) == 0);
    s = new CFString(_S("hello world"));
    assert(s->Compare(_S("hello world")) == 0);
    s = new CFString(_S("hello world"), 8, 36);
    assert(s->Compare(_S("hello wo")) == 0);
    assert(s->GetSize() == 36);
    
    CFString *a = new CFString(_S("123456"));
    CFString *b = new CFString(*a);
    assert(a->Compare(*b) == 0);
    assert(a->Equals(*b));

    *a = _S('\0');
    assert(a->GetLength() == 0);
}

static void TestCmpFindFunction(void)
{
    cppmempool::MemPoolWrap localPool;

    debug::Print(_S("Testing CmpFindFunction...\n"));

    CFString *a = new CFString("12345 67890 Abc");
    CFString *b = new CFString("12345 67890 abc_");
    assert(a->Compare(*b) < 0);
    
    b->Delete(b->GetLength() - 1);
    assert(b->Compare(_S("12345 67890 abc")) == 0);
    assert(a->CompareNoCase(*b) == 0);

    assert(b->Find(_S("45 ")) == 3);
    assert(b->Find(_S("*()")) < 0);
    assert(b->Find(_S('a')) == 12);
    assert(b->FindNoCase(_S("AbC")) == 12);
    assert(b->FindNoCase(_S("0 A")) == 10);
    assert(b->FindNoCase(_S("abCdefg")) < 0);
    assert(b->FindNoCase(_S("AB")) == 12);
    assert(b->FindNoCase(_S("BC")) == 13);
    assert(b->FindNoCase(_S("C")) == 14);
    assert(b->FindOneOf(_S("60c")) == 6);
    assert(b->ReverseFind(_S('2')) == 1);
    assert(b->ReverseFind(_S('%')) < 0);

    assert(a->StartsWith(_S('1')));
    assert(!a->StartsWith(_S('2')));
    assert(a->StartsWith(_S("1234")));
    assert(!a->StartsWith(_S("234")));

    assert(a->EndsWith(_S('c')));
    assert(!a->EndsWith(_S('b')));
    assert(a->EndsWith(_S(" Abc")));
    assert(!a->EndsWith(_S("Ab")));
}

static void TestModifyString(void)
{
    cppmempool::MemPoolWrap localPool;

    debug::Print(_S("Testing ModifyString...\n"));

    int ttt = 22334;
    int nttt = -67788;
    u32_t tInt = 0x99999999;
    u64_t bigint = U64N(9223372036854775809);
    u32_t *ppointer = (u32_t*)0x33335555;
    pcwstr_t pStr = _S("hello");
    CFString *a = new CFString("12abc");
    a->AppendFormat(_S(" %d %x %u %s %p %lld %llx %llu"), tInt, tInt, tInt, pStr, ppointer, bigint, bigint, bigint);
    assert(a->Compare(_S("12abc -1717986919 99999999 2576980377 hello 33335555 -9223372036854775807 8000000000000001 9223372036854775809")) == 0);

    a->Format(_S("test good %d %d %d %x %u %s %p %lld %llx %llu"), ttt, nttt, tInt, tInt, tInt, pStr, ppointer, bigint, bigint, bigint);
    assert(a->Compare(_S("test good 22334 -67788 -1717986919 99999999 2576980377 hello 33335555 -9223372036854775807 8000000000000001 9223372036854775809")) == 0);

    float f1 = static_cast<float>(33.00392);
    double f2 = 122.0002234;
    a->Format(_S("hello world, %.5f, %.7f"), f1, f2);
    assert(a->Compare(_S("hello world, 33.00392, 122.0002234")) == 0); 

    a->AppendFormat(_S(", %.80f"), f2);
    // Can't compare this since floating point number may be different on different platform
    //assert(a->Compare(_S("hello world, 33.00392, 122.0002234, 122.00022340000000000000000000000000000000000000000000000000000")) == 0); 
    assert(a->GetLength() == 99); 
    
    a->Format(_S("yes -- <<< %s, %S, %%S %c, %%C %C %C %C %C >>>"), _S("hello"), "world asdf ghjk zxcv bnmz", _S('Q'), 'z', 'q', 'a', 'x');
    assert(a->Compare(_S("yes -- <<< hello, world asdf ghjk zxcv bnmz, %S Q, %C z q a x >>>")) == 0); 

/*
 *  %e has no standard, it may be e+1 or e+01 or e+001. It is difficult to check here since
 *  CFString use "snprintf" to convert floating point to string internally.
 *    

    float f3 = 4.992;
    a->AppendFormat(_S(", %.7e, %.7e, %.6e"), f1, f2, f3);
    assert(a->Compare(_S("hello world, 33.00392, 122.0002234, 3.3300392e+1, 1.2200022e+2, 4.992000e+0")) == 0); 
*/
    CFString *b = new CFString("12abc");
    b->Insert(0, _S("pppp "));
    assert(b->Compare(_S("pppp 12abc")) == 0);
    b->Insert(5, "gogo");
    assert(b->Compare(_S("pppp gogo12abc")) == 0);
    b->Insert(1, _S('A'));
    assert(b->Compare(_S("pAppp gogo12abc")) == 0);
    b->Remove(_S('p'));
    assert(b->Compare(_S("A gogo12abc")) == 0);
    b->Replace(_S('g'), _S('3'));
    assert(b->Compare(_S("A 3o3o12abc")) == 0);
    b->Replace(_S("3o12"), _S("vvvv"));
    assert(b->Compare(_S("A 3ovvvvabc")) == 0);

    CFString *c = new CFString("Hello World");
    c->MakeLower();
    assert(c->Compare(_S("hello world")) == 0);
    c->MakeUpper();
    assert(c->Compare(_S("HELLO WORLD")) == 0);
    c->MakeReverse();
    assert(c->Compare(_S("DLROW OLLEH")) == 0);

    // trimming
    CFString *d = new CFString("  __123 abc 222 899__  ");
    d->Trim();
    assert(d->Compare(_S("__123 abc 222 899__")) == 0);
    d->Trim(_S('_'));
    assert(d->Compare(_S("123 abc 222 899")) == 0);
    d->Trim(_S("123"));
    assert(d->Compare(_S(" abc 222 899")) == 0);

    d = new CFString("  __123 abc 222 899__  ");
    d->TrimLeft();
    assert(d->Compare(_S("__123 abc 222 899__  ")) == 0);
    d->TrimLeft(_S('_'));
    assert(d->Compare(_S("123 abc 222 899__  ")) == 0);
    d->TrimLeft(_S("123"));
    assert(d->Compare(_S(" abc 222 899__  ")) == 0);

    d = new CFString("  __123 abc 222 899__  ");
    d->TrimRight();
    assert(d->Compare(_S("  __123 abc 222 899__")) == 0);
    d->TrimRight(_S('_'));
    assert(d->Compare(_S("  __123 abc 222 899")) == 0);
    d->TrimRight(_S("123"));
    assert(d->Compare(_S("  __123 abc 222 899")) == 0);
    d->TrimRight(_S("99"));
    assert(d->Compare(_S("  __123 abc 222 8")) == 0);

    d = new CFString("abc 222 899");
    d->Append(_S("2223 dddd"), 5);
    assert(d->Compare(_S("abc 222 8992223 ")) == 0);
    d->Append(_S("222"), 10);
    assert(d->Compare(_S("abc 222 8992223 222")) == 0);
    d->Append(_S("1111"), 4);
    assert(d->Compare(_S("abc 222 8992223 2221111")) == 0);
}

static void TestMiscFunc(void)
{
    cppmempool::MemPoolWrap localPool;

    debug::Print(_S("Testing MiscFunc...\n"));

    CFString *a = new CFString("1029384756");

    CFString b = a->Left(3);
    assert(b.Compare(_S("102")) == 0);
    assert(b.GetLength() == 3);
    b.SetAt(2, _S('\0'));
    b.SetLength(2);
    assert(b.Compare(_S("10")) == 0);
    assert(b.GetLength() == 2);
    assert(b[0] == _S('1'));
    assert(b[1] == _S('0'));
    try {
debug::Print(_S("Testing MiscFunc 1a...\n"));
       assert(b[2] == _S('\0'));
debug::Print(_S("Testing MiscFunc 1b...\n"));
       assert(false);
    }
    catch (exceptions::OutOfBoundsException &) {
    }

debug::Print(_S("Testing MiscFunc 1...\n"));

    b = a->Right(5);
    assert(b.Compare(_S("84756")) == 0);
    
    b = a->Mid(3, 3);
    assert(b.Compare(_S("938")) == 0);

    a = new CFString("paul,peter,mary,john,gary,");
    int start = 0;
    int i = 0;
    while (start >= 0) {
       CFString c = a->Tokenize(_S(","), start);
       switch (i) {
          case 0: assert(c.Compare(_S("paul")) == 0); break;
          case 1: assert(c.Compare(_S("peter")) == 0); break;
          case 2: assert(c.Compare(_S("mary")) == 0); break;
          case 3: assert(c.Compare(_S("john")) == 0); break;
          case 4: assert(c.Compare(_S("gary")) == 0); break;
          case 5: assert(c.IsEmpty()); break;
       }

       ++i;
    }

debug::Print(_S("Testing MiscFunc 2...\n"));

    // convert to UTF8
    char *buffer = NULL;
    int len = 0, len2 = 0;
    a->ToUtf8String(NULL, 0, &len);
    assert(len > 0);
    buffer = new (cppmempool::NEWPTR) char[len + 1];
    a->ToUtf8String(buffer, len + 1, &len2);
    assert(len == len2);

debug::Print(_S("Testing MiscFunc 3...\n"));

    buffer = NULL;
    len = 0;
    a->ToUtf8String(&buffer, &len);
    assert(len == len2);

    ObjPtrT<char> pCharPtr(a->ToUtf8String());
    assert(a->GetLength() == static_cast<int>(CFString::Strlen(pCharPtr)));
}

static void __ParseCmdString(collections::ListT<pcwstr_t> &argList, pcwstr_t args)
{
    if (args) {
      int lenArgs = (int)CFString::WStrlen(args);
      int index = 0;
      
      while (index < lenArgs) {
         wc_t c; 

         // skip all white spaces
         while (index < lenArgs) {
            c = args[index];
            if (c == _S(' ') || c == _S('\t'))
               ++index;
            else
               break;
         }

         if (index >= lenArgs)
            break;

         // get start and end of this argument
         int start = 0;
         bool isQuoted = false;

         if (c == _S('"')) {
            // deal with argument wrapped inside double quotes

            isQuoted = true;
            start = ++index;
            while (index < lenArgs) {
               c = args[index];
               if (c != _S('"'))
                  ++index;
               else
                  break;
            }

         }
         else {
            start = index;
            while (index < lenArgs) {
               c = args[index];
               if (c != _S(' ') && c != _S('\t'))
                  ++index;
               else
                  break;
            }

         }

         // copy it to the list
         if (start < index - 1) {
            wc_t * pStr = new (cppmempool::NEWPTR) wc_t[index - start + 1];
            CFString::Memcpy(pStr, args + start, (index - start) * sizeof(wc_t));
            argList.AddTail(pStr);
         }

         if (isQuoted)
            ++index;
      } // end of while
   }
}

static void TestParseCmdString()
{
   cppmempool::MemPoolWrap localPool;

   debug::Print(_S("Testing ParseCmdString...\n"));

   collections::ListT<pcwstr_t> argList;
   
   __ParseCmdString(argList, _S("1234"));
   assert(argList.GetCount() == 1);
   assert(CFString::WStrcmp(argList.Get(0), _S("1234")) == 0);
   argList.Clear();

   __ParseCmdString(argList, _S("1234 hello world"));
   assert(argList.GetCount() == 3);
   assert(CFString::WStrcmp(argList.Get(0), _S("1234")) == 0);
   assert(CFString::WStrcmp(argList.Get(1), _S("hello")) == 0);
   assert(CFString::WStrcmp(argList.Get(2), _S("world")) == 0);
   argList.Clear();

   __ParseCmdString(argList, _S("  1234 \"hello\" \"world\"  22 "));
   assert(argList.GetCount() == 4);
   assert(CFString::WStrcmp(argList.Get(0), _S("1234")) == 0);
   assert(CFString::WStrcmp(argList.Get(1), _S("hello")) == 0);
   assert(CFString::WStrcmp(argList.Get(2), _S("world")) == 0);
   assert(CFString::WStrcmp(argList.Get(3), _S("22")) == 0);
   argList.Clear();

   __ParseCmdString(argList, _S("  \"1234\" \"hel  lo\" \"world  "));
   assert(argList.GetCount() == 3);
   assert(CFString::WStrcmp(argList.Get(0), _S("1234")) == 0);
   assert(CFString::WStrcmp(argList.Get(1), _S("hel  lo")) == 0);
   assert(CFString::WStrcmp(argList.Get(2), _S("world  ")) == 0);
   argList.Clear();

   __ParseCmdString(argList, _S("\"go go \""));
   assert(argList.GetCount() == 1);
   assert(CFString::WStrcmp(argList.Get(0), _S("go go ")) == 0);
   argList.Clear();

   __ParseCmdString(argList, _S(""));
   assert(argList.GetCount() == 0);
   argList.Clear();

   __ParseCmdString(argList, _S("     "));
   assert(argList.GetCount() == 0);
   argList.Clear();
}

static void __TestPassCFString(pcwstr_t a, pcwstr_t b)
{
    assert(a != NULL);
    assert(CFString::WStrcmp(a, b) == 0);
}

static void TestOperatorOverload(void)
{
    cppmempool::MemPoolWrap localPool;

    debug::Print(_S("Testing OperatorOverload...\n"));

    CFString b = CFString("gogogo !");
    __TestPassCFString(b, _S("gogogo !"));

    CFString *a = new CFString("");
    *a = b;
    assert(a->Compare(_S("gogogo !")) == 0);
    __TestPassCFString(*a, _S("gogogo !"));

    *a = _S('P');
    assert(a->Compare(_S("P")) == 0);
    *a = _S("Happy birthday");
    assert(a->Compare(_S("Happy birthday")) == 0);
    *a = "Happy programming";
    assert(a->Compare(_S("Happy programming")) == 0);

    *a += b;
    assert(a->Compare(_S("Happy programminggogogo !")) == 0);
    *a += _S('Q');
    assert(a->Compare(_S("Happy programminggogogo !Q")) == 0);
    *a += _S("Happy birthday");
    assert(a->Compare(_S("Happy programminggogogo !QHappy birthday")) == 0);
    *a += "Happy programming";
    assert(a->Compare(_S("Happy programminggogogo !QHappy birthdayHappy programming")) == 0);

    CFString c = CFString("1230");
    assert((b + c).Compare(_S("gogogo !1230")) == 0);
    assert((b + _S('W')).Compare(_S("gogogo !W")) == 0);
    assert((_S('W') + b).Compare(_S("Wgogogo !")) == 0);
    assert((b + 'W').Compare(_S("gogogo !W")) == 0);
    assert(('W' + b).Compare(_S("Wgogogo !")) == 0);
    assert((b + _S("Woow!")).Compare(_S("gogogo !Woow!")) == 0);
    assert((_S("Woow!") + b).Compare(_S("Woow!gogogo !")) == 0);
    assert((b + "Woow!").Compare(_S("gogogo !Woow!")) == 0);
    assert(("Woow!" + b).Compare(_S("Woow!gogogo !")) == 0);

    // compare func
    CFString d = CFString("1230");
    assert(c == d);
    assert(c == _S("1230"));
    assert(_S("1230") == c);
    
    assert(c != b);
    assert(c != _S("cbvnm"));
    assert(_S("cbvnm") != c);
    
    assert(b > c);
    assert(b > _S("3445"));
    assert(_S("3445") > c);
    assert(!(_S("1230") > c));

    assert(c < b);
    assert(_S("3445") < b);
    assert(c < _S("3445"));
    assert(!(c < _S("1230")));

    c = _S("gogogo !");
    assert(b >= c);
    assert(b >= _S("gogogo !"));
    assert(_S("gogogo !") >= b);
    assert(_S("hogogo !") >= b);

    assert(c <= b);
    assert(_S("gogogo !") <= b);
    assert(b <= _S("gogogo !"));
    assert(b <= _S("kogogo !"));
    
}

static void TestStaticFunc(void)
{
    debug::Print(_S("Testing StaticFunction...\n"));

    assert(CFString::Strlen("haha haha") == 9);
    assert(CFString::WStrlen(_S("haha haha")) == 9);
    assert(CFString::WStrcmp(_S("123456"), _S("123456")) == 0);
    assert(CFString::WStrcmp(_S("1234567"), _S("123456")) > 0);
    assert(CFString::WStrcmp(_S("12345"), _S("123456")) < 0);
    assert(CFString::WStrncmp(_S("1234567"), _S("123456"), 6) == 0);
    assert(CFString::WStrncmp(_S("1234567"), _S("123456"), 7) > 0);
    assert(CFString::WStricmp(_S("123A4567"), _S("123a4567")) == 0);

    wc_t buf1[] = _S("haha");
    wc_t buf2[] = _S("    ");
    wc_t buf3[] = _S("  4567");
    wc_t buf4[] = _S("3344  ");
    CFString::Memcpy(buf2, buf1, 4 * sizeof(wc_t));
    assert(CFString::WStrcmp(buf2, _S("haha")) == 0);
    CFString::Memcpy(buf3, &buf3[2], 5 * sizeof(wc_t));
    assert(CFString::WStrcmp(buf3, _S("4567")) == 0);
    CFString::Memcpy(&buf4[2], buf4, 4 * sizeof(wc_t));
    assert(CFString::WStrcmp(buf4, _S("333344")) == 0);
}


void TestCFString()
{
    debug::Print(_S("Testing CFString...\n"));

    TestNewCFString();
    TestCmpFindFunction();
    TestModifyString();
    TestMiscFunc();
    TestParseCmdString();
    TestOperatorOverload();
    TestStaticFunc();
}
